#![allow(non_snake_case, non_camel_case_types, dead_code)]
use jnim::{JInt, JVm, JVoid};

mod hello {
	use std::ops::Deref;

	use jnim::{CachedID, JClassID, JEnv, JInt, JNameMaker, JObject, JPlainMarker, JVm};

	struct Hello {
		__supper: JObject,
		__myinterface: MyInterfaceObject,
	}
	impl Deref for Hello {
		type Target = JObject;
		fn deref(&self) -> &Self::Target {
			&self.__supper
		}
	}
	impl AsRef<JObject> for Hello {
		fn as_ref(&self) -> &JObject {
			&self.__supper
		}
	}
	impl JNameMaker for Hello {
		const JAVA_PATH: &'static [u8] = b"Hello\0";
	}
	impl JPlainMarker for Hello {
		fn class(env: Option<&JEnv>) -> Option<JClassID> {
			static CACHE: CachedID = CachedID::new();
			CACHE.get(|| JEnv::env(env)?.find_class(Self::JAVA_PATH))
		}
	}
	impl Hello {
		fn play(&self, env: &JEnv) -> Option<()> {
			static CACHE: CachedID = CachedID::new();
			CACHE
				.get(|| Self::class(Some(env))?.method(env, b"play\0", b"()V\0"))?
				.call(env, self.as_ref(), ())
		}
	}

	impl MyInterface for Hello {}
	impl AsRef<MyInterfaceObject> for Hello {
		fn as_ref(&self) -> &MyInterfaceObject {
			&self.__myinterface
		}
	}

	trait MyInterface: AsRef<JObject> {
		fn MyInterface_class(env: Option<&JEnv>) -> Option<JClassID> {
			const MY_INTERFACE_NAME: &'static [u8] = b"MyInterface\0";
			static CACHE: CachedID = CachedID::new();
			CACHE.get(|| env.unwrap_or(JVm::singleton(None)?.env()?).find_class(MY_INTERFACE_NAME))
		}
		fn bar(&self, env: &JEnv) -> Option<()> {
			static CACHE: CachedID = CachedID::new();
			CACHE
				.get(|| Self::MyInterface_class(Some(env))?.method(env, b"bar\0", b"()V\0"))?
				.call(env, self.as_ref(), ())
		}
	}
	pub struct MyInterfaceObject {
		inner: JObject,
	}
	impl MyInterface for MyInterfaceObject {}
	impl AsRef<JObject> for MyInterfaceObject {
		fn as_ref(&self) -> &JObject {
			&self.inner
		}
	}

	#[no_mangle]
	#[allow(non_snake_case)]
	extern "system" fn Java_Hello_print(env: &JEnv, hello: &Hello, i: JInt) {
		println!("[Rust] Hello print called {i}");
		hello.play(env);
	}
	#[no_mangle]
	#[allow(non_snake_case)]
	extern "system" fn Java_HelloOne_printone(env: &JEnv, hello: &Hello) {
		println!("[Rust] Hello printone called");
		hello.play(env);
		hello.bar(env);
	}
	#[no_mangle]
	#[allow(non_snake_case)]
	extern "system" fn Java_HelloTwo_printtwo(env: &JEnv, hello: &Hello) {
		println!("[Rust] Hello printtwo called");
		hello.play(env);
		hello.bar(env);
	}
}
mod world {
	use jnim::{CachedID, JClassID, JEnv, JInt, JNameMaker, JObject, JPlainMarker};

	#[no_mangle]
	#[allow(non_snake_case)]
	extern "system" fn Java_World_print(env: &JEnv, world: &World, i: JInt) {
		println!("[Rust] World print called {i}");
		world.play(env);
		world.get_state(env).map(|value| world.set_state(env, value + 100));
	}

	struct World {
		__supper: JObject,
	}
	impl AsRef<JObject> for World {
		fn as_ref(&self) -> &JObject {
			&self.__supper
		}
	}
	impl JNameMaker for World {
		const JAVA_PATH: &'static [u8] = b"World\0";
	}
	impl JPlainMarker for World {
		fn class(env: Option<&JEnv>) -> Option<JClassID> {
			static CACHE: CachedID = CachedID::new();
			CACHE.get(|| JEnv::env(env)?.find_class(Self::JAVA_PATH))
		}
	}
	impl World {
		fn get_state(&self, env: &JEnv) -> Option<JInt> {
			static CACHE: CachedID = CachedID::new();
			CACHE
				.get(|| Self::class(Some(env))?.field(env, b"state\0", b"I\0"))?
				.get(env, self.as_ref())
		}
		fn set_state(&self, env: &JEnv, value: JInt) -> Option<()> {
			static CACHE: CachedID = CachedID::new();
			CACHE
				.get(|| Self::class(Some(env))?.field(env, b"state\0", b"I\0"))?
				.set(env, self.as_ref(), value)
		}
		fn play(&self, env: &JEnv) -> Option<()> {
			static CACHE: CachedID = CachedID::new();
			CACHE
				.get(|| Self::class(Some(env))?.method(env, b"play\0", b"()V\0"))?
				.call(env, self.as_ref(), ())
		}
	}
}

#[no_mangle]
#[allow(non_snake_case)]
extern "system" fn JNI_OnLoad(vm: JVm, _: &JVoid) -> JInt {
	JVm::singleton(Some(vm));
	JVm::DEFAULT_VERSION
}
